import { Game_Character } from "@/objects/GameCharacter.js";
import { Game_Followers } from "@/objects/GameFollowers.js"
import { contains } from "@/utils/Array.js";
import { config, ConfigManager } from "@/managers/ConfigManager.js";
import { BattleManager } from "@/managers/BattleManager.js";
import { Input } from "@/core/Input.js";
import { TouchInput } from "@/core/TouchInput.js";
export class Game_Player extends Game_Character {
    constructor() {
        super();
        this._vehicleType = 'walk';
        this._vehicleGettingOn = false;
        this._vehicleGettingOff = false;
        this._dashing = false;
        this._needsMapReload = false;
        this._transferring = false;
        this._newMapId = 0;
        this._newX = 0;
        this._newY = 0;
        this._newDirection = 0;
        this._fadeType = 0;
        this._followers = new Game_Followers();
        this._encounterCount = 0;
        this.setTransparent($dataSystem.optTransparent);
    }

    clearTransferInfo() {
        this._transferring = false;
        this._newMapId = 0;
        this._newX = 0;
        this._newY = 0;
        this._newDirection = 0;
    }

    followers() {
        return this._followers;
    }

    refresh() {
        let actor = $gameParty.leader();
        let characterName = actor ? actor.characterName() : '';
        let characterIndex = actor ? actor.characterIndex() : 0;
        this.setImage(characterName, characterIndex);
        this._followers.refresh();
    }

    isStopping() {
        if (this._vehicleGettingOn || this._vehicleGettingOff) {
            return false;
        }
        return Game_Character.prototype.isStopping.call(this);
    }

    reserveTransfer(mapId, x, y, d, fadeType) {
        this._transferring = true;
        this._newMapId = mapId;
        this._newX = x;
        this._newY = y;
        this._newDirection = d;
        this._fadeType = fadeType;
    }

    requestMapReload() {
        this._needsMapReload = true;
    }

    isTransferring() {
        return this._transferring;
    }

    newMapId() {
        return this._newMapId;
    }

    fadeType() {
        return this._fadeType;
    }

    performTransfer() {
        if (this.isTransferring()) {
            this.setDirection(this._newDirection);
            if (this._newMapId !== $gameMap.mapId() || this._needsMapReload) {
                $gameMap.setup(this._newMapId);
                this._needsMapReload = false;
            }
            this.locate(this._newX, this._newY);
            this.refresh();
            this.clearTransferInfo();
        }
    }

    isMapPassable(x, y, d) {
        let vehicle = this.vehicle();
        if (vehicle) {
            return vehicle.isMapPassable(x, y, d);
        } else {
            return Game_Character.prototype.isMapPassable.call(this, x, y, d);
        }
    }

    vehicle() {
        return $gameMap.vehicle(this._vehicleType);
    }

    isInBoat() {
        return this._vehicleType == 'boat';
    }

    isInShip() {
        return this._vehicleType == 'ship';
    }

    isInAirship() {
        return this._vehicleType == 'airship';
    }

    isInVehicle() {
        return this.isInBoat() || this.isInShip() || this.isInAirship();
    }

    isNormal() {
        return this._vehicleType == 'walk' && !this.isMoveRouteForcing();
    }

    isDashing() {
        return this._dashing;
    }

    isDebugThrough() {
        return Input.isPressed('control') && $gameTemp.isPlaytest();
    }

    isCollided(x, y) {
        if (this.isThrough()) {
            return false;
        } else {
            return this.pos(x, y) || this._followers.isSomeoneCollided(x, y);
        }
    }

    centerX() {
        return (config.width / $gameMap.tileWidth() - 1) / 2.0;
    }

    centerY() {
        return (config.height / $gameMap.tileHeight() - 1) / 2.0;
    }

    center(x, y) {
        return $gameMap.setDisplayPos(x - this.centerX(), y - this.centerY());
    }

    locate(x, y) {
        super.locate(x, y);
        this.center(x, y);
        this.makeEncounterCount();
        if (this.isInVehicle()) {
            this.vehicle().refresh();
        }
        this._followers.synchronize(x, y, this.direction());
    }

    increaseSteps() {
        super.increaseSteps();
        if (this.isNormal()) {
            $gameParty.increaseSteps();
        }
    }

    makeEncounterCount() {
        let n = $gameMap.encounterStep();
        this._encounterCount = Math.random() * n + Math.random() * n + 1;
    }

    makeEncounterTroopId() {
        let encounterList = [];
        let weightSum = 0;
        $gameMap.encounterList().forEach(function (encounter) {
            if (this.meetsEncounterConditions(encounter)) {
                encounterList.push(encounter);
                weightSum += encounter.weight;
            }
        }, this);
        if (weightSum > 0) {
            let value = Math.random() * weightSum;
            for (let i = 0; i < encounterList.length; i++) {
                value -= encounterList[i].weight;
                if (value < 0) {
                    return encounterList[i].troopId;
                }
            }
        }
        return 0;
    }

    meetsEncounterConditions(encounter) {
        return (encounter.regionSet.length == 0 ||
            contains(encounter.regionSet, this.regionId()));
    }

    executeEncounter() {
        if (!$gameMap.isEventRunning() && this._encounterCount <= 0) {
            this.makeEncounterCount();
            let troopId = this.makeEncounterTroopId();
            if ($dataTroops[troopId]) {
                BattleManager.setup(troopId, true, false);
                BattleManager.onEncounter();
                return true;
            } else {
                return false;
            }
        } else {
            return false;
        }
    }

    startMapEvent(x, y, triggers, normal) {
        if (!$gameMap.isEventRunning()) {
            $gameMap.eventsXy(x, y).forEach(function (event) {
                if (event.isTriggerIn(triggers) && event.isNormalPriority() == normal) {
                    event.start();
                }
            });
        }
    }

    moveByInput() {
        if (!this.isMoving() && this.canMove()) {
            let direction = this.getInputDirection();
            if (direction > 0) {
                $gameTemp.clearDestination();
            } else if ($gameTemp.isDestinationValid()) {
                let x = $gameTemp.destinationX();
                let y = $gameTemp.destinationY();
                direction = this.findDirectionTo(x, y);
            }
            if (direction > 0) {
                this.executeMove(direction);
            }
        }
    }

    canMove() {
        if ($gameMap.isEventRunning() || $gameMessage.isBusy()) {
            return false;
        }
        if (this.isMoveRouteForcing() || this.areFollowersGathering()) {
            return false;
        }
        if (this._vehicleGettingOn || this._vehicleGettingOff) {
            return false;
        }
        if (this.isInVehicle() && !this.vehicle().canMove()) {
            return false;
        }
        return true;
    }

    getInputDirection() {
        return Input.dir4;
    }

    executeMove(direction) {
        this.moveStraight(direction);
    }

    update(sceneActive) {
        let lastScrolledX = this.scrolledX();
        let lastScrolledY = this.scrolledY();
        let wasMoving = this.isMoving();
        this.updateDashing();
        if (sceneActive) {
            this.moveByInput();
        }
        super.update();
        this.updateScroll(lastScrolledX, lastScrolledY);
        this.updateVehicle();
        if (!this.isMoving()) {
            this.updateNonmoving(wasMoving);
        }
        this._followers.update();
    }

    updateDashing() {
        if (this.isMoving()) {
            return;
        }
        if (this.canMove() && !this.isInVehicle() && !$gameMap.isDashDisabled()) {
            this._dashing = this.isDashButtonPressed() || $gameTemp.isDestinationValid();
        } else {
            this._dashing = false;
        }
    }

    isDashButtonPressed() {
        let shift = Input.isPressed('shift');
        if (ConfigManager.alwaysDash) {
            return !shift;
        } else {
            return shift;
        }
    }

    updateScroll(lastScrolledX, lastScrolledY) {
        let x1 = lastScrolledX;
        let y1 = lastScrolledY;
        let x2 = this.scrolledX();
        let y2 = this.scrolledY();
        if (y2 > y1 && y2 > this.centerY()) {
            $gameMap.scrollDown(y2 - y1);
        }
        if (x2 < x1 && x2 < this.centerX()) {
            $gameMap.scrollLeft(x1 - x2);
        }
        if (x2 > x1 && x2 > this.centerX()) {
            $gameMap.scrollRight(x2 - x1);
        }
        if (y2 < y1 && y2 < this.centerY()) {
            $gameMap.scrollUp(y1 - y2);
        }
    }

    updateVehicle() {
        if (this.isInVehicle() && !this.areFollowersGathering()) {
            if (this._vehicleGettingOn) {
                this.updateVehicleGetOn();
            } else if (this._vehicleGettingOff) {
                this.updateVehicleGetOff();
            } else {
                this.vehicle().syncWithPlayer();
            }
        }
    }

    updateVehicleGetOn() {
        if (!this.areFollowersGathering() && !this.isMoving()) {
            this.setDirection(this.vehicle().direction());
            this.setMoveSpeed(this.vehicle().moveSpeed());
            this._vehicleGettingOn = false;
            this.setTransparent(true);
            if (this.isInAirship()) {
                this.setThrough(true);
            }
            this.vehicle().getOn();
        }
    }

    updateVehicleGetOff() {
        if (!this.areFollowersGathering() && this.vehicle().isLowest()) {
            this._vehicleGettingOff = false;
            this._vehicleType = 'walk';
            this.setTransparent(false);
        }
    }

    updateNonmoving(wasMoving) {
        if (!$gameMap.isEventRunning()) {
            if (wasMoving) {
                $gameParty.onPlayerWalk();
                this.checkEventTriggerHere([1, 2]);
                if ($gameMap.setupStartingEvent()) {
                    return;
                }
            }
            if (this.triggerAction()) {
                return;
            }
            if (wasMoving) {
                this.updateEncounterCount();
            } else {
                $gameTemp.clearDestination();
            }
        }
    }

    triggerAction() {
        if (this.canMove()) {
            if (this.triggerButtonAction()) {
                return true;
            }
            if (this.triggerTouchAction()) {
                return true;
            }
        }
        return false;
    }

    triggerButtonAction() {
        if (Input.isTriggered('ok')) {
            if (this.getOnOffVehicle()) {
                return true;
            }
            this.checkEventTriggerHere([0]);
            if ($gameMap.setupStartingEvent()) {
                return true;
            }
            this.checkEventTriggerThere([0, 1, 2]);
            if ($gameMap.setupStartingEvent()) {
                return true;
            }
        }
        return false;
    }

    triggerTouchAction() {
        if ($gameTemp.isDestinationValid()) {
            let direction = this.direction();
            let x1 = this.x;
            let y1 = this.y;
            let x2 = $gameMap.roundXWithDirection(x1, direction);
            let y2 = $gameMap.roundYWithDirection(y1, direction);
            let x3 = $gameMap.roundXWithDirection(x2, direction);
            let y3 = $gameMap.roundYWithDirection(y2, direction);
            let destX = $gameTemp.destinationX();
            let destY = $gameTemp.destinationY();
            if (destX == x1 && destY == y1) {
                return this.triggerTouchActionD1(x1, y1);
            } else if (destX == x2 && destY == y2) {
                return this.triggerTouchActionD2(x2, y2);
            } else if (destX == x3 && destY == y3) {
                return this.triggerTouchActionD3(x2, y2);
            }
        }
        return false;
    }

    triggerTouchActionD1(x1, y1) {
        if ($gameMap.airship().pos(x1, y1)) {
            if (TouchInput.isTriggered() && this.getOnOffVehicle()) {
                return true;
            }
        }
        this.checkEventTriggerHere([0]);
        return $gameMap.setupStartingEvent();
    }

    triggerTouchActionD2(x2, y2) {
        if ($gameMap.boat().pos(x2, y2) || $gameMap.ship().pos(x2, y2)) {
            if (TouchInput.isTriggered() && this.getOnVehicle()) {
                return true;
            }
        }
        if (this.isInBoat() || this.isInShip()) {
            if (TouchInput.isTriggered() && this.getOffVehicle()) {
                return true;
            }
        }
        this.checkEventTriggerThere([0, 1, 2]);
        return $gameMap.setupStartingEvent();
    }

    triggerTouchActionD3(x2, y2) {
        if ($gameMap.isCounter(x2, y2)) {
            this.checkEventTriggerThere([0, 1, 2]);
        }
        return $gameMap.setupStartingEvent();
    }

    updateEncounterCount() {
        if (this.canEncounter()) {
            this._encounterCount -= this.encounterProgressValue();
        }
    }

    canEncounter() {
        return (!$gameParty.hasEncounterNone() && $gameSystem.isEncounterEnabled() &&
            !this.isInAirship() && !this.isMoveRouteForcing() && !this.isDebugThrough());
    }

    encounterProgressValue() {
        let value = $gameMap.isBush(this.x, this.y) ? 2 : 1;
        if ($gameParty.hasEncounterHalf()) {
            value *= 0.5;
        }
        if (this.isInShip()) {
            value *= 0.5;
        }
        return value;
    }

    checkEventTriggerHere(triggers) {
        if (this.canStartLocalEvents()) {
            this.startMapEvent(this.x, this.y, triggers, false);
        }
    }

    checkEventTriggerThere(triggers) {
        if (this.canStartLocalEvents()) {
            let direction = this.direction();
            let x1 = this.x;
            let y1 = this.y;
            let x2 = $gameMap.roundXWithDirection(x1, direction);
            let y2 = $gameMap.roundYWithDirection(y1, direction);
            this.startMapEvent(x2, y2, triggers, true);
            if (!$gameMap.isAnyEventStarting() && $gameMap.isCounter(x2, y2)) {
                let x3 = $gameMap.roundXWithDirection(x2, direction);
                let y3 = $gameMap.roundYWithDirection(y2, direction);
                this.startMapEvent(x3, y3, triggers, true);
            }
        }
    }

    checkEventTriggerTouch(x, y) {
        if (this.canStartLocalEvents()) {
            this.startMapEvent(x, y, [1, 2], true);
        }
    }

    canStartLocalEvents() {
        return !this.isInAirship();
    }

    getOnOffVehicle() {
        if (this.isInVehicle()) {
            return this.getOffVehicle();
        } else {
            return this.getOnVehicle();
        }
    }

    getOnVehicle() {
        let direction = this.direction();
        let x1 = this.x;
        let y1 = this.y;
        let x2 = $gameMap.roundXWithDirection(x1, direction);
        let y2 = $gameMap.roundYWithDirection(y1, direction);
        if ($gameMap.airship().pos(x1, y1)) {
            this._vehicleType = 'airship';
        } else if ($gameMap.ship().pos(x2, y2)) {
            this._vehicleType = 'ship';
        } else if ($gameMap.boat().pos(x2, y2)) {
            this._vehicleType = 'boat';
        }
        if (this.isInVehicle()) {
            this._vehicleGettingOn = true;
            if (!this.isInAirship()) {
                this.forceMoveForward();
            }
            this.gatherFollowers();
        }
        return this._vehicleGettingOn;
    }

    getOffVehicle() {
        if (this.vehicle().isLandOk(this.x, this.y, this.direction())) {
            if (this.isInAirship()) {
                this.setDirection(2);
            }
            this._followers.synchronize(this.x, this.y, this.direction());
            this.vehicle().getOff();
            if (!this.isInAirship()) {
                this.forceMoveForward();
                this.setTransparent(false);
            }
            this._vehicleGettingOff = true;
            this.setMoveSpeed(4);
            this.setThrough(false);
            this.makeEncounterCount();
            this.gatherFollowers();
        }
        return this._vehicleGettingOff;
    }

    forceMoveForward() {
        this.setThrough(true);
        this.moveForward();
        this.setThrough(false);
    }

    isOnDamageFloor() {
        return $gameMap.isDamageFloor(this.x, this.y) && !this.isInAirship();
    }

    moveStraight(d) {
        if (this.canPass(this.x, this.y, d)) {
            this._followers.updateMove();
        }
        super.moveStraight(d);
    }

    moveDiagonally(horz, vert) {
        if (this.canPassDiagonally(this.x, this.y, horz, vert)) {
            this._followers.updateMove();
        }
        super.moveDiagonally(horz, vert);
    }

    jump(xPlus, yPlus) {
        super.jump(xPlus, yPlus);
        this._followers.jumpAll();
    }

    showFollowers() {this._followers.show();}
    hideFollowers() {this._followers.hide();}
    gatherFollowers() {this._followers.gather();}
    areFollowersGathering() {return this._followers.areGathering();}
    areFollowersGathered() {return this._followers.areGathered();}
    
    serialize() {
        return {
            ...super.serialize(),
            vehicleType: this._vehicleType,
            vehicleGettingOn: this._vehicleGettingOn,
            vehicleGettingOff: this._vehicleGettingOff,
            dashing: this._dashing,
            needsMapReload: this._needsMapReload,
            transferring: this._transferring,
            newMapId: this._newMapId,
            newX: this._newX,
            newY: this._newY,
            newDirection: this._newDirection,
            fadeType: this._fadeType,
            encounterCount: this._encounterCount,
            followers: this._followers.serialize()
        }
    }

    deserialize(data) {
        super.deserialize(data);
        this._vehicleType = data.vehicleType;
        this._vehicleGettingOn = data.vehicleGettingOn;
        this._vehicleGettingOff = data.vehicleGettingOff;
        this._dashing = data.dashing;
        this._needsMapReload = data.needsMapReload;
        this._transferring = data.transferring;
        this._newMapId = data.newMapId;
        this._newX = data.newX;
        this._newY = data.newY;
        this._newDirection = data.newDirection;
        this._fadeType = data.fadeType;
        this._encounterCount = data.encounterCount;
        this._followers.deserialize(data.followers);
    }
}